package jdklearning.nio.socket;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 选择器是NIO技术的核心组件（缓冲区Buffer、通道Channel、选择器Selector）之一。选择器的主要作用是使用1个线程来对多个通道中的已经就绪通道进行选择，选择出就绪的通道后，就可以对通道中的数据进行处理。选择器核心类包括Selector、SelectionKey、SelectableChannel。
 *
 * 通道的标记SelectionKey
 * 每个注册到选择器中的通道，都有一个代表通道的SelectionKey对象。
 *
 * 可选择通道SelectableChannel
 * ServerSocketChannel
 * SocketChannel
 * DatagramChannel
 * SctpChannel
 * SctpMultiChannel
 * SctpServerChannel
 * SinkChannel
 * SourceChannel
 * 通道的操作类型
 * OP_ACCEPT
 * OP_CONNECT
 * OP_READ
 * OP_WRITE
 *
 * @author shenenlu 2021年03月08日 下午23:25:10
 */
public class SelectorFileTransmissionServer {


    public static void main(String[] args) {
        try {
            ServerSocketChannel channel = ServerSocketChannel.open();
            //设置为非阻塞模式
            channel.configureBlocking(false);
            channel.bind(new InetSocketAddress("localhost",8088));
            //打开选择器
            Selector selector = Selector.open();
            //注册连接通道
            channel.register(selector, SelectionKey.OP_ACCEPT);
            while (true){
                //选择已就绪的key
                selector.select();
                //获取已就绪的key的集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()){
                    SelectionKey readyKey = iterator.next();
                    iterator.remove();
                    //判断当前已就绪key是否是连接通道的key
                    if(readyKey.isAcceptable()) {
                        //接收客户端的连接
                        SocketChannel accept = channel.accept();
                        accept.configureBlocking(false);
                        //注册读取客户端数据的通道
                        accept.register(selector, SelectionKey.OP_WRITE);
                    }
                    //判断当前key是否是可写入的通道
                    if(readyKey.isWritable()){
                        SocketChannel socketChannel = (SocketChannel) readyKey.channel();
                        File file = new File("D:\\test001.txt");
                        FileInputStream fileInputStream = new FileInputStream(file);
                        FileChannel fileChannel = fileInputStream.getChannel();
                        // 设置帧头写入缓冲区，回写消息给客户端
                        boolean isFrame=true;
                        while (fileChannel.position()<fileChannel.size()){
                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                            // 读取要发送的文件的内容，放入缓冲区
                            if(isFrame){
                                // 设置是否包含frame
                                byteBuffer.putInt(0);
                                // 写入文件名长度到帧头缓冲区 int为4字节
                                byteBuffer.putInt(file.getName().length());
                                // 写入文件名到帧头缓冲区
                                byteBuffer.put(file.getName().getBytes());
                                // 写入文件文件大小到帧头缓冲区
                                byteBuffer.putLong(fileChannel.size());
                                isFrame=false;
                            }else {
                                byteBuffer.putInt(1);
                            }
                            fileChannel.read(byteBuffer);
                            // 将文件内容写入缓冲区
                            byteBuffer.flip();
                            socketChannel.write(byteBuffer);
                            byteBuffer.clear();
                        }
                        socketChannel.close();
                        fileChannel.close();
                    }

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}
